﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace SongeNS
{
    public static class VectorUtil
    {
        /// <summary>获取法向量</summary>
        public static Vector3 GetNormalVector(Vector3 va, Vector3 vb, Vector3 vc)
        {
            //平面方程Ax+BY+CZ+d=0 行列式计算
            float A = va.y * vb.z + vb.y * vc.z + vc.y * va.z - va.y * vc.z - vb.y * va.z - vc.y * vb.z;
            float B = -(va.x * vb.z + vb.x * vc.z + vc.x * va.z - vc.x * vb.z - vb.x * va.z - va.x * vc.z);
            float C = va.x * vb.y + vb.x * vc.y + vc.x * va.y - va.x * vc.y - vb.x * va.y - vc.x * vb.y;
            //float D = -(va.x * vb.y * vc.z + vb.x * vc.y * va.z + vc.x * va.y * vb.z - va.x * vc.y * vb.z - vb.x * va.y * vc.z - vc.x * vb.y * va.z);
            float E = Mathf.Sqrt(A * A + B * B + C * C);
            Vector3 res = new Vector3(A / E, B / E, C / E);
            return (res);
        }

        public static Vector3 GetScaledVector(this Vector3 tar, float multi)
        {
            return new Vector3(tar.x * multi, tar.y * multi, tar.z * multi);
        }

        public static Bounds? GetBounds(GameObject go)
        {
            Bounds? bounds = null;
            //bounds.
            Bounds ab;

            SongeUtil.forAllChildren(go, tar =>
            {
                if (tar.GetComponent<Renderer>() != null)
                {
                    Bounds b = tar.GetComponent<Renderer>().bounds;
                    if (bounds == null)
                        bounds = b;
                    else
                    {
                        ab = bounds.Value;//对bounds.Value的改变不能作用到bounds上 需要中间变量
                        ab.Encapsulate(b);
                        bounds = ab;
                    }
                }
            });
            return bounds;
        }

        public static Vector3 GetMoveInput(bool allowArrow = true)
        {
            float kz = 0;
            if (allowArrow)
            {
                kz += Input.GetKey(KeyCode.UpArrow) ? 1f : 0f * 1f;
                kz += Input.GetKey(KeyCode.DownArrow) ? -1f : 0f * 1f;
            }
            kz += Input.GetKey(KeyCode.W) ? 1f : 0f * 1f;
            kz += Input.GetKey(KeyCode.S) ? -1f : 0f * 1f;
            float kx = 0;
            if (allowArrow)
            {
                kx -= Input.GetKey(KeyCode.LeftArrow) ? -1f : 0f * 1f;
                kx -= Input.GetKey(KeyCode.RightArrow) ? 1f : 0f * 1f;
            }
            kx -= Input.GetKey(KeyCode.A) ? -1f : 0f * 1f;
            kx -= Input.GetKey(KeyCode.D) ? 1f : 0f * 1f;
            return new Vector3(kx, 0, kz);
        }


        /// <summary>获取朝目标方向前进后退左右平移后, 坐标的改变</summary>
        /// <param name="rotH">水平面旋转方向</param>
        /// <param name="dirX">左右移动距离</param>
        public static Vector3 MoveTowards(float rotH, float dirX, float dirZ)
        {
            float dx = dirX * Mathf.Sin(rotH) + dirZ * Mathf.Cos(rotH);
            float dz = dirX * Mathf.Cos(rotH) - dirZ * Mathf.Sin(rotH);
            return new Vector3(dx, 0, dz);
        }
        public static Vector3 MoveTowards(float rotH, Vector3 dir)
        {
            float dx = dir.x * Mathf.Sin(rotH) + dir.z * Mathf.Cos(rotH);
            float dz = dir.x * Mathf.Cos(rotH) - dir.z * Mathf.Sin(rotH);
            return new Vector3(dx, 0, dz);
        }

        /// <summary>返回目标向量在X-Z平面旋转(增加)angle(弧度)后的单位向量</summary>
        public static Vector3 RotateVecotrAtXZ(Vector3 tar, float angle)
        {
            float ang = Mathf.Atan2(tar.z, tar.x);
            ang = ang + angle;
            float newX = 1f * Mathf.Cos(ang);
            float newZ = 1f * Mathf.Sin(ang);
            return new Vector3(newX, tar.y, newZ); ;
        }

        #region 扩展方法
        public enum EnumVector { x, y, z }
        public static void SetLocalPos(this GameObject tar, float value, EnumVector dir)
        {
            SetLocalPos(tar.transform, value, dir);
        }
        public static void SetLocalPos(this Transform tar, float value, EnumVector dir)
        {
            float newValueX = dir == EnumVector.x ? value : tar.localPosition.x;
            float newValueY = dir == EnumVector.y ? value : tar.localPosition.y;
            float newValueZ = dir == EnumVector.z ? value : tar.localPosition.z;
            tar.localPosition = new Vector3(newValueX, newValueY, newValueZ);
        }

        public static void SetVec(ref Vector3 tar, float value, EnumVector dir)
        {
            float newValueX = dir == EnumVector.x ? value : tar.x;
            float newValueY = dir == EnumVector.y ? value : tar.y;
            float newValueZ = dir == EnumVector.z ? value : tar.z;
            tar.Set(newValueX, newValueY, newValueZ);
        }

        #endregion

        /// <summary>
        /// 靠近目标点
        /// </summary>
        /// <param name="curPos">当前位置</param>
        /// <param name="tarPos">目标点位置</param>
        /// <param name="moveDis">最大移动距离</param>
        /// <returns>剩余的移动能力, 若到达, 则大于等于0</returns>
        public static float MoveToPoint(Vector3 curPos, Vector3 tarPos, float moveDis, ref Vector3 nextPos)
        {
            Vector3 dir = tarPos - curPos;
            dir = dir.normalized;
            float distance = Vector3.Distance(tarPos, curPos);
            if (distance > moveDis)
            {
                nextPos = curPos + dir.GetScaledVector(moveDis);
                return 0;
            }
            else
            {
                nextPos = tarPos;
                return distance - moveDis;
            }
        }

        //示例 eularAngle = new Vector3( -GetRotatV, GetRotateY, 0)
        /// <summary>返回Vector3(0, 0, 1)指向目标向量, Y轴需要旋转角度(360)</summary>
        public static float GetRotateY(Vector3 dir)
        {
            return Mathf.Atan2(dir.x, dir.z) / Mathf.PI * 180;
        }
        //示例 eularAngle = new Vector3( -GetRotatV, GetRotateY, 0)
        /// <summary>获取仰角</summary>
        public static float GetRotatV(Vector3 dir)
        {
            float len2 = dir.x * dir.x + dir.z * dir.z;
            float len = Mathf.Sqrt(len2);
            float atan = Mathf.Atan2(dir.y, len);
            return atan / Mathf.PI * 180;
        }
    }

    #region PerspectiveTransform 变换矩阵
    public class PerspectiveTransform
    {
        float a11;
        float a12;
        float a13;
        float a21;
        float a22;
        float a23;
        float a31;
        float a32;
        float a33;
        public PerspectiveTransform(float inA11, float inA21,
                                           float inA31, float inA12,
                                           float inA22, float inA32,
                                           float inA13, float inA23,
                                           float inA33)
        {
            a11 = inA11;
            a12 = inA12;
            a13 = inA13;

            a21 = inA21;
            a22 = inA22;
            a23 = inA23;

            a31 = inA31;
            a32 = inA32;
            a33 = inA33;
        }

        public static PerspectiveTransform quadrilateralToQuadrilateral(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float x0p, float y0p, float x1p, float y1p, float x2p, float y2p, float x3p, float y3p)
        {
            PerspectiveTransform qToS = quadrilateralToSquare(x0, y0, x1, y1, x2, y2, x3, y3);
            PerspectiveTransform sToQ = squareToQuadrilateral(x0p, y0p, x1p, y1p, x2p, y2p, x3p, y3p);
            return sToQ.times(qToS);
        }

        static PerspectiveTransform squareToQuadrilateral(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)
        {
            float dx3 = x0 - x1 + x2 - x3;
            float dy3 = y0 - y1 + y2 - y3;
            if (dx3 == 0.0f && dy3 == 0.0f)
            {
                return new PerspectiveTransform(x1 - x0, x2 - x1, x0, y1 - y0, y2 - y1, y0, 0.0f, 0.0f, 1.0f);
            }
            else
            {
                float dx1 = x1 - x2;
                float dx2 = x3 - x2;
                float dy1 = y1 - y2;
                float dy2 = y3 - y2;
                float denominator = dx1 * dy2 - dx2 * dy1;
                float a13 = (dx3 * dy2 - dx2 * dy3) / denominator;
                float a23 = (dx1 * dy3 - dx3 * dy1) / denominator;
                return new PerspectiveTransform(x1 - x0 + a13 * x1, x3 - x0 + a23 * x3, x0, y1 - y0 + a13 * y1, y3 - y0 + a23 * y3, y0, a13, a23, 1.0f);
            }
        }

        static PerspectiveTransform quadrilateralToSquare(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3)
        {
            return squareToQuadrilateral(x0, y0, x1, y1, x2, y2, x3, y3).buildAdjoint();
        }

        PerspectiveTransform buildAdjoint()
        {
            return new PerspectiveTransform(a22 * a33 - a23 * a32, a23 * a31 - a21 * a33, a21 * a32
                                   - a22 * a31, a13 * a32 - a12 * a33, a11 * a33 - a13 * a31, a12 * a31 - a11 * a32, a12 * a23 - a13 * a22,
                                   a13 * a21 - a11 * a23, a11 * a22 - a12 * a21);
        }

        PerspectiveTransform times(PerspectiveTransform other)
        {
            return new PerspectiveTransform(a11 * other.a11 + a21 * other.a12 + a31 * other.a13,
                                   a11 * other.a21 + a21 * other.a22 + a31 * other.a23, a11 * other.a31 + a21 * other.a32 + a31
                                   * other.a33, a12 * other.a11 + a22 * other.a12 + a32 * other.a13, a12 * other.a21 + a22
                                   * other.a22 + a32 * other.a23, a12 * other.a31 + a22 * other.a32 + a32 * other.a33, a13
                                   * other.a11 + a23 * other.a12 + a33 * other.a13, a13 * other.a21 + a23 * other.a22 + a33
                                   * other.a23, a13 * other.a31 + a23 * other.a32 + a33 * other.a33);
        }

        public void transformPoints(List<float> points)
        {
            int max = points.Count;
            for (int i = 0; i < max; i += 2)
            {
                float x = points[i];
                float y = points[i + 1];
                float denominator = a13 * x + a23 * y + a33;
                points[i] = (a11 * x + a21 * y + a31) / denominator;
                points[i + 1] = (a12 * x + a22 * y + a32) / denominator;
            }
        }


    }

    #endregion

}

